

<!DOCTYPE html>
<html lang="zh-CN" color-mode=light>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>springMybatis - 唱跳rap</title>
  <meta name="apple-mobile-web-app-capable" content="yes" />
  <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
  <meta name="google" content="notranslate" />
  
  <meta name="description" content="Springspring框架 Spring是一个轻量级...">
  <meta name="author" content="东子">
  <link rel="icon" href="/images/icons/icon-29.png" type="image/png" sizes="16x16">
  <link rel="icon" href="/images/icons/icon-29@2x.png" type="image/png" sizes="32x32">
  <link rel="apple-touch-icon" href="/images/icons/icon-1024.png" sizes="180x180">
  <meta rel="mask-icon" href="/images/icons/stun-logo.svg" color="#333333">
  
    <meta rel="msapplication-TileImage" content="/images/icons/icon-1024.png">
    <meta rel="msapplication-TileColor" content="#000000">
  

  
<link rel="stylesheet" href="/css/style.css">


  
    
<link rel="stylesheet" href="https://at.alicdn.com/t/font_1445822_p6ry5n7lrr.css">

  

  
    
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fancybox/3.5.7/jquery.fancybox.min.css">

  

  
    
      
        
        
<link rel="stylesheet" href="https://cdn.bootcss.com/highlight.js/9.18.1/styles/xcode.min.css" name="highlight-style" mode="light">

      
        
        
<link rel="stylesheet" href="https://cdn.bootcss.com/highlight.js/9.18.1/styles/solarized-dark.min.css" name="highlight-style" mode="dark">

      
  

  <script>
    var CONFIG = window.CONFIG || {};
    var ZHAOO = window.ZHAOO || {};
    CONFIG = {
      isHome: false,
      fancybox: true,
      pjax: false,
      loading: {
        gif: '',
        lottie: ''
      },
      lazyload: {
        enable: true,
        only_post: 'false',
        loading: {
          gif: '',
          lottie: ''
        }
      },
      donate: {
        enable: false,
        alipay: 'https://pic.izhaoo.com/alipay.jpg',
        wechat: 'https://pic.izhaoo.com/wechat.jpg'
      },
      galleries: {
        enable: false
      },
      fab: {
        enable: true,
        always_show: false
      },
      carrier: {
        enable: true
      },
      daovoice: {
        enable: false
      },
      preview: {
        background: {
          default: '',
          api: 'https://api.paugram.com/bing/'
        },
        motto: {
          default: '我在开了灯的床头下，想问问自己的心啊。',
          typing: true,
          api: 'https://v2.jinrishici.com/one.json',
          data_contents: '["data","content"]'
        },
      },
      qrcode: {
        enable: true,
        type: 'url',
        image: 'https://pic.izhaoo.com/weapp-code.jpg',
      },
      toc: {
        enable: true
      },
      scrollbar: {
        type: 'default'
      },
      notification: {
        enable: false,
        delay: 4500,
        list: '',
        page_white_list: '',
        page_black_list: ''
      },
      search: {
        enable: false,
        path: ''
      }
    }
  </script>

  

  

<meta name="generator" content="Hexo 6.3.0"></head>

<body class="lock-screen">
  <div class="loading" id="loading"></div>
  
    


  <nav class="navbar">
    <div class="left">
      
        <i class="iconfont iconhome j-navbar-back-home"></i>
      
      
        <i class="iconfont iconqrcode j-navbar-qrcode"></i>
      
      
        <i class="iconfont iconmoono" id="color-toggle" color-toggle="light"></i>
      
      
    </div>
    <div class="center">springMybatis</div>
    <div class="right">
      <i class="iconfont iconmenu j-navbar-menu"></i>
    </div>
    
      <div id="qrcode-navbar"></div>
    
  </nav>

  
  

<nav class="menu">
  <div class="menu-container">
    <div class="menu-close">
      <i class="iconfont iconbaseline-close-px"></i>
    </div>
    <ul class="menu-content"><li class="menu-item">
        <a href="/ " class="underline "> 首页</a>
      </li><li class="menu-item">
        <a href="/archives/ " class="underline "> 归档</a>
      </li><li class="menu-item">
        <a href="/categories/ " class="underline "> 分类</a>
      </li><li class="menu-item">
        <a href="/about/ " class="underline "> 关于</a>
      </li></ul>
    
      <div class="menu-copyright"><p>Powered by <a target="_blank" href="https://gitee.com/comebackearly">东子</a>  | 爷-青结~~</p></div>
    
  </div>
</nav>
  <main id="main">
  <div class="article-wrap">
    <div class="row container">
      <div class="col-xl-3"></div>
      <div class="col-xl-6"><article class="article">
  <div class="wrap">
    <section class="head">
  <img   class="lazyload" data-original="https://tse1-mm.cn.bing.net/th/id/OIP-C.CHQ2_5Kgl49KPWFhk9PIXAHaEo?pid=ImgDet&rs=1" src=""  draggable="false">
  <div class="head-mask">
    <h1 class="head-title">springMybatis</h1>
    <div class="head-info">
      <span class="post-info-item"><i class="iconfont iconcalendar"></i>八月 16, 2023</span>
      
      <span class="post-info-item"><i class="iconfont iconfont-size"></i>10111</span>
    </div>
  </div>
</section>
    <section class="main">
      <section class="content">
        
        <h1 id="Spring"><a href="#Spring" class="headerlink" title="Spring"></a>Spring</h1><h2 id="spring框架"><a href="#spring框架" class="headerlink" title="spring框架"></a>spring框架</h2><p> Spring是一个轻量级的框架，通过<strong>IOC达到松耦合</strong>的目的，使用AOP可以不改变原有逻辑的同时对业务进行增强</p>
<p> 使用Spring之后可以让我们不用过多关注底层的技术细节，而是更加关注业务上的逻辑的实现</p>
<h2 id="什么是IOC"><a href="#什么是IOC" class="headerlink" title="什么是IOC"></a>什么是IOC</h2><p> IOC，即控制反转，<strong>把对象的创建、初始化、销毁交给 Spring 来管理</strong>，而不是由开发者控制，实现控制反转。IOC 思想基于 IOC 容器完成，IOC 容器底层就是对象工厂（<a target="_blank" rel="noopener" href="https://so.csdn.net/so/search?q=BeanFactory&spm=1001.2101.3001.7020">BeanFactory</a> 接口）。IOC的原理是基于xml解析、工厂设计模式、反射实现的。使用IOC可以降低代码的耦合度。</p>
<h2 id="IOC-容器实现两种方式"><a href="#IOC-容器实现两种方式" class="headerlink" title="IOC 容器实现两种方式"></a><strong>IOC 容器实现两种方式</strong></h2><p>（1）BeanFactory：IOC 容器基本实现，是 Spring 内部的使用接口，不提供开发人员进行使用。</p>
<p>在加载配置文件时候不会创建对象，在获取对象（使用）才去创建对象</p>
<p>（2）ApplicationContext：BeanFactory 接口的子接口，提供更多更强大的功能，一般由开发人</p>
<p>员进行使用。在加载配置文件时候就会把在配置文件对象进行创建</p>
<h2 id="什么是AOP"><a href="#什么是AOP" class="headerlink" title="什么是AOP"></a>什么是AOP</h2><blockquote>
<p>AOP称为面向切面编程，用于将那些与业务无关，但却<strong>对多个对象产生影响的公共行为</strong>和逻辑，<strong>抽取并封装为一个可 重用的模块</strong>，这个模块被命名为“切面”（Aspect），<strong>减少系统中的重复代码，降低了模块间的耦合度</strong>，同时提高 了系统的可维护性,提高程序的可重用性.</p>
</blockquote>
<ul>
<li>记录日志</li>
<li>spring内置事务</li>
<li>抽取模块</li>
</ul>
<h2 id="Aop的底层原理"><a href="#Aop的底层原理" class="headerlink" title="Aop的底层原理"></a>Aop的底层原理</h2><p>AOP底层是使用<strong>动态代理</strong>来实现的，这里有两种情况的动态代理：</p>
<p>① <strong>被代理的类有接口的情况，使用 JDK 动态代理</strong></p>
<p>即创建接口实现类代理对象，增强类的方法。</p>
<p>② 被代理的类有<strong>没有实现接口的情况，使用 CGLIB 动态代理</strong></p>
<p>即创建子类的代理对象，增强类的方法。</p>
<h2 id="AOP的专业术语"><a href="#AOP的专业术语" class="headerlink" title="AOP的专业术语"></a><strong>AOP的专业术语</strong></h2><p>① 连接点：类里面可以被增强的方法，这些方法被称为连接点。</p>
<p>② <strong>切入点</strong>：实际被真正增强的方法，称为切入点。明确对哪个类里面的哪个方法进行增强。</p>
<p>③ 通知（增强）：（1）实际增强的逻辑部分称为通知（增强）</p>
<p>​               （2）通知有多种类型：前置通知、后置通知、环绕通知、异常通知、最终通知</p>
<p>④ 切面：把通知应用到切入点的过程，称为切面。</p>
<h2 id="AOP实现日志收集"><a href="#AOP实现日志收集" class="headerlink" title="AOP实现日志收集"></a>AOP实现日志收集</h2><p>① Spring 框架一般都是基于 AspectJ 实现 AOP 操作</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springfarmework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-aop<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>自定义注解</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> cn.annotation;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.*;</span><br><span class="line"> </span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> V1.0</span></span><br><span class="line"><span class="comment"> * Description:  自定义注解，拦截controller</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> </span><br><span class="line"><span class="meta">@Target(&#123;ElementType.PARAMETER, ElementType.METHOD&#125;)</span><span class="comment">//作用在参数和方法上</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span><span class="comment">//运行时注解</span></span><br><span class="line"><span class="meta">@Documented</span><span class="comment">//表明这个注解应该被 javadoc工具记录</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> SystemControllerLog &#123;</span><br><span class="line">    String <span class="title function_">description</span><span class="params">()</span> <span class="keyword">default</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>这里就需要去写aop的切面类了</p>
<figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line">@Aspect</span><br><span class="line">@Component</span><br><span class="line">@<span class="constructor">SuppressWarnings(<span class="string">&quot;all&quot;</span>)</span></span><br><span class="line">public <span class="keyword">class</span> SystemLogAspect &#123;</span><br><span class="line">    <span class="comment">//注入Service用于把日志保存数据库，实际项目入库采用队列做异步</span></span><br><span class="line">    @Resource</span><br><span class="line">    <span class="keyword">private</span> ActionService actionService;</span><br><span class="line">    <span class="comment">//本地异常日志记录对象</span></span><br><span class="line">    <span class="keyword">private</span> static final Logger logger = <span class="module-access"><span class="module"><span class="identifier">LoggerFactory</span>.</span></span>get<span class="constructor">Logger(SystemLogAspect.<span class="params">class</span>)</span>;</span><br><span class="line">    <span class="comment">//Service层切点</span></span><br><span class="line">    @<span class="constructor">Pointcut(<span class="string">&quot;@annotation(cn.annotation.SystemServiceLog)&quot;</span>)</span></span><br><span class="line">    public void service<span class="constructor">Aspect()</span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line">    <span class="comment">//Controller层切点</span></span><br><span class="line">    @<span class="constructor">Pointcut(<span class="string">&quot;@annotation(cn.oa.annotation.SystemControllerLog)&quot;</span>)</span></span><br><span class="line">    public void controller<span class="constructor">Aspect()</span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * @Description  前置通知  用于拦截Controller层记录用户的操作</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line"> </span><br><span class="line">    @<span class="constructor">Before(<span class="string">&quot;controllerAspect()&quot;</span>)</span></span><br><span class="line">    public void <span class="keyword">do</span><span class="constructor">Before(JoinPoint <span class="params">joinPoint</span>)</span>&#123;</span><br><span class="line">        HttpServletRequest request = ((ServletRequestAttributes) <span class="module-access"><span class="module"><span class="identifier">RequestContextHolder</span>.</span></span>get<span class="constructor">RequestAttributes()</span>).get<span class="constructor">Request()</span>;</span><br><span class="line">        HttpSession session = request.get<span class="constructor">Session()</span>;</span><br><span class="line">        <span class="comment">//读取session中的用户,需要在请求同中携带用户的信息才能获取到数据，博主的token使用中有介绍如何携带使用</span></span><br><span class="line">        <span class="comment">//User user = (User) session.getAttribute(&quot;user&quot;);</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="string">&quot;==============前置通知开始==============&quot;</span>);</span><br><span class="line">            <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="string">&quot;请求方法&quot;</span> + (joinPoint.get<span class="constructor">Target()</span>.get<span class="constructor">Class()</span>.get<span class="constructor">Name()</span> + <span class="string">&quot;.&quot;</span> + joinPoint.get<span class="constructor">Signature()</span>.get<span class="constructor">Name()</span>));</span><br><span class="line">            <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="string">&quot;方法描述：&quot;</span> + get<span class="constructor">ControllerMethodDescription(<span class="params">joinPoint</span>)</span>);</span><br><span class="line">           <span class="comment">// System.out.println(&quot;请求人：&quot;+user.getUsername());</span></span><br><span class="line">            <span class="comment">//*========数据库日志=========*//</span></span><br><span class="line">            <span class="comment">//保存数据库(这里可以直接调用service将收集到的日志信息存到数据库中)</span></span><br><span class="line">        &#125;catch (Exception e)&#123;</span><br><span class="line">            <span class="comment">//记录本地异常日志</span></span><br><span class="line">            logger.error(<span class="string">&quot;==前置通知异常==&quot;</span>);</span><br><span class="line">            logger.error(<span class="string">&quot;异常信息：&#123;&#125;&quot;</span>,e.get<span class="constructor">Message()</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>

<h2 id="统一异常处理"><a href="#统一异常处理" class="headerlink" title="统一异常处理"></a>统一异常处理</h2><p>原理：@ControllerAdvice+@ExceptionHandler</p>
<p>@ControllerAdvice注解是Spring3.2中新增的注解，学名是Controller增强器，作用是给Controller控制器添加统一的操作或处理。</p>
<p>@ControllerAdvice的用法基本是将其声明在某个bean上，然后在该bean的方法上使用其他的注解来指定不同的织入逻辑。不过这里@ControllerAdvice并不是使用AOP的方式来织入业务逻辑的，而是Spring内置对其各个逻辑的织入方式进行了内置支持。</p>
<p>统一异常处理:项目代码:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.qiu.servicebase.exceptionhandler;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.qiu.commonutils.R;</span><br><span class="line"><span class="keyword">import</span> org.springframework.web.bind.<span class="keyword">annotation</span>.ControllerAdvice;</span><br><span class="line"><span class="keyword">import</span> org.springframework.web.bind.<span class="keyword">annotation</span>.ExceptionHandler;</span><br><span class="line"><span class="keyword">import</span> org.springframework.web.bind.<span class="keyword">annotation</span>.ResponseBody;</span><br><span class="line"></span><br><span class="line"><span class="meta">@ControllerAdvice</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GlobalExceptionHandler</span> &#123;</span><br><span class="line">    <span class="comment">//指定出现了什么异常执行这个方法</span></span><br><span class="line">    <span class="meta">@ExceptionHandler(Exception.class)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span> <span class="comment">//为了能够返回数据</span></span><br><span class="line">    <span class="keyword">public</span> R error(Exception e)&#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">        <span class="keyword">return</span> R.error().message(<span class="string">&quot;执行了全局异常处理&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>特定异常处理</p>
<figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">  <span class="comment">//特定异常处理</span></span><br><span class="line">    <span class="variable">@ExceptionHandler</span>(ArithmeticException.class)</span><br><span class="line">    <span class="variable">@ResponseBody</span> <span class="comment">//为了能够返回数据</span></span><br><span class="line">    public R <span class="built_in">error</span>(ArithmeticException e)&#123;</span><br><span class="line">        <span class="selector-tag">e</span><span class="selector-class">.printStackTrace</span>();</span><br><span class="line">        <span class="selector-tag">return</span> <span class="selector-tag">R</span><span class="selector-class">.error</span>()<span class="selector-class">.message</span>(<span class="string">&quot;执行了ArithmeticException处理&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">特定的就是根据全局的修改部分代码.执行顺序是先找特定的异常处理,再找全局异常处理</span><br></pre></td></tr></table></figure>

<h2 id="Spring中的事务是怎么实现的"><a href="#Spring中的事务是怎么实现的" class="headerlink" title="Spring中的事务是怎么实现的"></a>Spring中的事务是怎么实现的</h2><blockquote>
<p>Spring支持编程式事务管理和声明式事务管理两种方式</p>
</blockquote>
<ul>
<li><p>声明式事务(@Around)</p>
<p>声明式事务管理：声明式事务管理建立在<strong>AOP</strong>之上的。其本质是<strong>通过AOP功能，对方法前后进行拦截</strong>，将事务处理的 功能编织到拦截的方法中，<strong>也就是在目标方法开始之前加入一个事务</strong>，<strong>在执行完目标方法之后根据执行情况提交或 者回滚事务</strong></p>
</li>
</ul>
<h2 id="Spring中事务失效的场景"><a href="#Spring中事务失效的场景" class="headerlink" title="Spring中事务失效的场景"></a>Spring中事务失效的场景</h2><blockquote>
<p>一般有三种情况，<strong>被异常捕获处理，被抛出检查异常 ，用于非public方法</strong></p>
</blockquote>
<ul>
<li><p><strong>异常捕获处理</strong>：手动捕获异常，事务通知无法知悉。一般需要在catch块添加**throw new RuntimeException(e)**抛出</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Transactional</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">update</span><span class="params">(Integer from, Integer to, Double money)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">try</span></span><br><span class="line">    &#123;</span><br><span class="line">		...业务逻辑</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">catch</span>(Exception e)</span><br><span class="line">    &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>抛出检查异常：Spring 默认只会回滚非检查异常。配置rollbackFor属性@Transactional(<strong>rollbackFor&#x3D;Exception.class</strong>)</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Transactional</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">update</span><span class="params">(Integer from, Integer to, Double money)</span> <span class="keyword">throws</span> FileNotFoundException</span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">File</span> <span class="variable">f</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileInputStream</span>(<span class="string">&quot;不存在的文件&quot;</span>);<span class="comment">//不捕获，直接抛出</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>非public方法：Spring 为方法创建代理、添加事务通知、前提条件都是该 <strong>方法是 public</strong> </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Transactional(rollbackFor = Exception.class)</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">update</span><span class="params">(Integer from, Integer to, Double money)</span> <span class="keyword">throws</span> FileNotFoundException<span class="comment">//必须是public方法才能使用事务功能</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">//转账的用户不能为空</span></span><br><span class="line">    <span class="type">Account</span> <span class="variable">fromAccount</span> <span class="operator">=</span> accountDao.selectById(from);</span><br><span class="line">    <span class="comment">//判断用户的钱是否够转账</span></span><br><span class="line">    <span class="keyword">if</span>(fromAccount.getMoney() - money &gt;= <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        fromAccount.setMoney(fromAccount.getMoney() - money);</span><br><span class="line">        accountDao.updateById(fromAccount);</span><br><span class="line">        <span class="comment">//读取文件</span></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">FileInputStream</span>(<span class="string">&quot;dddd&quot;</span>);</span><br><span class="line">        <span class="comment">//被转账的用户</span></span><br><span class="line">        <span class="type">Account</span> <span class="variable">toAccount</span> <span class="operator">=</span> accountDao.selectById(to);</span><br><span class="line">        toAccount.setMoney(toAccount.getMoney() + money);</span><br><span class="line">        accountDao.updateById(toAccount);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
<h2 id="Spring事务的传播"><a href="#Spring事务的传播" class="headerlink" title="Spring事务的传播"></a>Spring事务的传播</h2><blockquote>
<p>方法A是一个事务的方法，方法A执行过程中调用了方法B，那么方法B有无事务以及方法B对事务的要求不同都会对方法A的事务具体执行造成影响，同时方法A的事务对方法B的事务执行也有影响，这种影响具体是什么就由两个方法所定义的事务传播类型所决定。</p>
</blockquote>
<p><strong>REQUIRED</strong>、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER、NESTED。</p>
<p>默认就是<strong>REQUIRED</strong>：<strong>如果当前A没有事务，则自己新建一个事务，如果当前A存在事务，则加入这个事务</strong></p>
<h2 id="Spring-Bean的生命周期"><a href="#Spring-Bean的生命周期" class="headerlink" title="Spring Bean的生命周期"></a>Spring Bean的生命周期</h2><ul>
<li><p>BeanDefinition</p>
<blockquote>
<p>Spring容器在进行实例化时，会<strong>将xml配置的的信息封装成一个BeanDefinition对象</strong>，Spring根据 BeanDefinition来创建Bean对象</p>
</blockquote>
</li>
</ul>
<p><img   class="lazyload" data-original="/image-20230817113744647.png" src=""  alt="image-20230817113744647"></p>
<p>① 通过BeanDefinition获取bean的定义信息 </p>
<p>② 调用构造函数实例化bean </p>
<p>③ bean的依赖注入 </p>
<p>④ 处理Aware接口(BeanNameAware、BeanFactoryAware、ApplicationContextAware) </p>
<p>⑤ Bean的后置处理器BeanPostProcessor-前置 </p>
<p>⑥ 初始化方法(InitializingBean、init-method)</p>
<p> ⑦ Bean的后置处理器BeanPostProcessor-后置</p>
<p> ⑧ 销毁bean</p>
<h2 id="Spring中的循环依赖"><a href="#Spring中的循环依赖" class="headerlink" title="Spring中的循环依赖"></a>Spring中的循环依赖</h2><blockquote>
<p>在创建A对象的同时需要使用的B对象，在创建B对象的同时需要使用到A对象</p>
</blockquote>
<h2 id="SpringMvc的执行流程"><a href="#SpringMvc的执行流程" class="headerlink" title="SpringMvc的执行流程"></a>SpringMvc的执行流程</h2><p><img   class="lazyload" data-original="/image-20230817114054974.png" src=""  alt="image-20230817114054974"></p>
<p> ①用户发送出请求到前端控制器<strong>DispatcherServlet</strong></p>
<p> ② DispatcherServlet收到请求调用<strong>HandlerMapping</strong>（处理器映射器）</p>
<p> ③ HandlerMapping找到具体的处理器，<strong>生成处理器对象及处理器拦截器</strong>(如果有)，再一起返回 给DispatcherServlet。</p>
<p> ④ DispatcherServlet调用<strong>HandlerAdapter（处理器适配器）</strong> </p>
<p>⑤ HandlerAdapter经过适配调用具体的<strong>处理器（Handler&#x2F;Controller）</strong> </p>
<p>⑥ 通过<strong>HttpMessageConverter</strong>来返回结果转换为JSON并响应</p>
<h2 id="SpringBoot的自动装配"><a href="#SpringBoot的自动装配" class="headerlink" title="SpringBoot的自动装配"></a>SpringBoot的自动装配</h2><blockquote>
<p>SpringBoot框架最核心的思想</p>
</blockquote>
<ul>
<li>@SpringBootApplication是由三个注解组成的： @SpringBootConfiguration， @ComponentScan， @EnableAutoConfiguration<ul>
<li>l @SpringBootConfiguration：该注解与 @Configuration 注解作用相同，用来声明当前也是一个<strong>配置类</strong>。 </li>
<li>l @ComponentScan：<strong>组件扫描</strong>，默认扫描当前引导类所在包及其子包。</li>
<li>l <strong>@EnableAutoConfiguration</strong>：<strong>SpringBoot实现自动化配置的核心注解。</strong></li>
</ul>
</li>
</ul>
<p><strong>@EnableAutoConfiguration</strong>注解通过**@Import注解导入对应的配置选择器<strong>。 内部就是读取了该项目和该项目引用的Jar包的的classpath路径下META-INF&#x2F;<strong>spring.factories</strong>文件中的所配置的类</strong>的全类名<strong>。 在这些配置 类中所定义的Bean会根据条件注解所指定的条件来决定是否需要将其</strong>导入到Spring容器中。**</p>
<p> 条件判断会有像@ConditionalOnClass这样的注解，判断是否有对应的class文件，如果有则加载该类，把这个配置类的所有的Bean放入 spring容器中使用。</p>
<h2 id="Sping，SpringMvc-SpringBoot的常见注解"><a href="#Sping，SpringMvc-SpringBoot的常见注解" class="headerlink" title="Sping，SpringMvc,SpringBoot的常见注解"></a>Sping，SpringMvc,SpringBoot的常见注解</h2><ul>
<li>Spring</li>
</ul>
<table>
<thead>
<tr>
<th align="center">@Component、@Controller、@Service、@Repository</th>
<th align="center">使用在类上用于实例化Bean</th>
</tr>
</thead>
<tbody><tr>
<td align="center">@Autowired @Qualifier</td>
<td align="center">使用在字段上用于根据类型依赖注入</td>
</tr>
<tr>
<td align="center">@Scope</td>
<td align="center">标注Bean的作用范围</td>
</tr>
<tr>
<td align="center">@Configuration</td>
<td align="center">指定当前类是一个 Spring 配置类,当创建容器时会从该类上加载注解</td>
</tr>
<tr>
<td align="center">@ComponentScan</td>
<td align="center">用于指定 Spring 在初始化容器时要扫描的包</td>
</tr>
<tr>
<td align="center">@Bean</td>
<td align="center">使用在方法上，标注将该方法的返回值存储到Spring容器中</td>
</tr>
<tr>
<td align="center">@Import</td>
<td align="center">使用@Import导入的类会被Spring加载到IOC容器中</td>
</tr>
</tbody></table>
<ul>
<li>SpringMvc</li>
</ul>
<table>
<thead>
<tr>
<th align="center">注解</th>
<th align="center">说明</th>
</tr>
</thead>
<tbody><tr>
<td align="center">@RequestMapping</td>
<td align="center">用于映射请求路径，可以定义在类上和方法上。用于类上，则表示类中的所有的方法都是以该地址作为父路径</td>
</tr>
<tr>
<td align="center">@RequestBody</td>
<td align="center">实现接收HTTP请求的JSON数据，将JSON转换为Java对象</td>
</tr>
<tr>
<td align="center">@RequestParam</td>
<td align="center">指定请求参数的名称</td>
</tr>
<tr>
<td align="center">@PathVariable</td>
<td align="center">从请求路径中获取请求参数(<code>/user/&#123;id&#125;</code>)，并传递给方法的形式参数</td>
</tr>
<tr>
<td align="center">@ResponseBody</td>
<td align="center">实现将Controller方法返回的对象转化为JSON对象响应给客户端</td>
</tr>
<tr>
<td align="center">@RequestHeader</td>
<td align="center">获取指定的请求头数据</td>
</tr>
<tr>
<td align="center">@RestController</td>
<td align="center">组合了@Controller和@ResponseBody注解，用于标识控制器类并将方法返回的对象转化为JSON对象响应给客户端</td>
</tr>
</tbody></table>
<ul>
<li><p>SpringBoot</p>
<p>@SpringBootConfiguration, @EnableAutoConfiguration</p>
</li>
</ul>
<h1 id="Mybatis"><a href="#Mybatis" class="headerlink" title="Mybatis"></a>Mybatis</h1><h2 id="MyBatis执行流程"><a href="#MyBatis执行流程" class="headerlink" title="MyBatis执行流程"></a>MyBatis执行流程</h2><blockquote>
<p>参数映射、sql解析、执行和结果处理</p>
</blockquote>
<p><img   class="lazyload" data-original="/image-20230821153442172.png" src=""  alt="image-20230821153442172"></p>
<p>① 读取<strong>MyBatis配置</strong>文件：mybatis-config.xml加载运行环境和映射文件</p>
<p>② 构造会话工厂<strong>SqlSessionFactory</strong></p>
<p>③ 会话工厂创建<strong>SqlSession对象</strong>（包含了执行SQL语句的所有方法）</p>
<p>④ 操作数据库的接口，<strong>Executor执行器</strong>，同时负责查询缓存的维护 </p>
<p>⑤ Executor接口的执行方法中有一个MappedStatement类型的参数，封装了<strong>映射</strong>信息</p>
<p>⑥ 输入参数映射 </p>
<p>⑦ 输出结果映</p>
<h2 id="Mybatis的延迟加载和一二级缓存"><a href="#Mybatis的延迟加载和一二级缓存" class="headerlink" title="Mybatis的延迟加载和一二级缓存"></a>Mybatis的延迟加载和一二级缓存</h2><ul>
<li><p>Mybatis是否支持延迟加载</p>
<blockquote>
<p>Mybatis配置文件中，可以配置是否启用延迟加载lazyLoadingEnabled&#x3D;true|false，<strong>默认是关闭</strong>的</p>
</blockquote>
<p>原理：</p>
<p>1.使用CGLIB创建目标对象的<strong>代理</strong>对象</p>
<ol start="2">
<li>当调用目标方法时，进入拦截器invoke方法，发现目标方法是null值，执行sql查询 </li>
<li>获取数据以后，调用set方法设置属性值，再继续查询目标方法，就有值了</li>
</ol>
</li>
<li><p><strong>一级缓存</strong></p>
<blockquote>
<p>一级缓存: 基于 PerpetualCache 的 <strong>HashMap 本地缓存</strong>，其存储作用域为 <strong>Session</strong>，当Session进行flush或close之 后，该Session中的所有Cache就将清空，<strong>默认打开一级缓存</strong></p>
</blockquote>
<p>只会查询一次sql</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//2. 获取SqlSession对象，用它来执行sql</span></span><br><span class="line"><span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> sqlSessionFactory.openSession();</span><br><span class="line"><span class="comment">//3. 执行sql</span></span><br><span class="line"><span class="comment">//3.1 获取UserMapper接口的代理对象</span></span><br><span class="line"><span class="type">UserMapper</span> <span class="variable">userMapper1</span> <span class="operator">=</span> sqlSession.getMapper(UserMapper.class);</span><br><span class="line"><span class="type">UserMapper</span> <span class="variable">userMapper2</span> <span class="operator">=</span> sqlSession.getMapper(UserMapper.class);</span><br><span class="line"><span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> userMapper1.selectById(<span class="number">6</span>);</span><br><span class="line">System.out.println(user);</span><br><span class="line">System.out.println(<span class="string">&quot;---------------------&quot;</span>);</span><br><span class="line"><span class="type">User</span> <span class="variable">user1</span> <span class="operator">=</span> userMapper2.selectById(<span class="number">6</span>);</span><br><span class="line">System.out.println(user1);</span><br></pre></td></tr></table></figure>
</li>
<li><p><strong>二级缓存</strong></p>
<blockquote>
<p>二级缓存是<strong>基于namespace和mapper的作用域</strong>起作用的，不是依赖于SQL session，默认也是采用 PerpetualCache， <strong>HashMap</strong> 存</p>
</blockquote>
<ul>
<li><p>开启方式：</p>
<p>开启方式，两步： 1，全局配置文件 2，映射文件 使用标签让当前mapper生效二级缓存</p>
</li>
</ul>
<p>1，对于缓存数据更新机制，当某一个作用域(一级缓存 Session&#x2F;二级缓存Namespaces)的进行了<strong>新增、修改、删除</strong>操作后，默认该 作用域下所有 select 中的缓存将被 clear </p>
<p>2，<strong>二级缓存需要缓存的数据实现Serializable接口</strong> </p>
<p>3，只有会话提交或者关闭以后，一级缓存中的数据才会转移到二级缓存中</p>
</li>
</ul>
<h2 id="Mybatis的-he-的区别"><a href="#Mybatis的-he-的区别" class="headerlink" title="Mybatis的#{}he ${}的区别"></a>Mybatis的#{}he ${}的区别</h2><ol>
<li><code>#</code> 参数注入方式：<ul>
<li>使用<code>#</code>可以<strong>防止SQL注入</strong>攻击。</li>
<li>例如，<code>SELECT * FROM table WHERE id = #&#123;id&#125;</code> 中的 <code>#&#123;id&#125;</code> 将会被替换为 <code>SELECT * FROM table WHERE id = ?</code>，并且传递的参数值会被安全地注入。</li>
</ul>
</li>
<li><code>$</code> 参数注入方式：<ul>
<li>使用<code>$</code>进行参数注入时，是直接将参数值拼接到SQL语句中。</li>
<li>例如，<code>SELECT * FROM table WHERE id = $&#123;id&#125;</code> 中的 <code>$&#123;id&#125;</code> 将会被替换为 <code>SELECT * FROM table WHERE id = 1</code>（假设传递的参数值为1）。</li>
</ul>
</li>
</ol>
<blockquote>
<p>尽量使用#，但是，在某些特定的情况下，使用<code>$</code>可以更灵活地构建动态SQL语句，例如表名、列名等的拼接。</p>
</blockquote>

      </section>
      <section class="extra">
        
          <ul class="copyright">
  
    <li><strong>本文作者：</strong>东子</li>
    <li><strong>本文链接：</strong><a href="https://comebackearly.gitee.io/2023/08/16/springMybatis/index.html" title="https:&#x2F;&#x2F;comebackearly.gitee.io&#x2F;2023&#x2F;08&#x2F;16&#x2F;springMybatis&#x2F;index.html">https:&#x2F;&#x2F;comebackearly.gitee.io&#x2F;2023&#x2F;08&#x2F;16&#x2F;springMybatis&#x2F;index.html</a></li>
    <li><strong>版权声明：</strong>本博客所有文章均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" title="BY-NC-SA" target="_blank" rel="noopener">BY-NC-SA</a> 许可协议，转载请注明出处！</li>
  
</ul>
        
        
        
        
  <nav class="nav">
    <a href="/2023/08/21/Jvm/"><i class="iconfont iconleft"></i>Jvm</a>
    <a href="/2023/07/01/%E8%A7%86%E9%A2%91/">视频<i class="iconfont iconright"></i></a>
  </nav>

      </section>
      
        <section class="comments">
  
    <div class="btn" id="comments-btn">查看评论</div>
  
  
<div id="valine"></div>
<script defer src="//unpkg.com/valine/dist/Valine.min.js"></script>
<script>
  window.onload = function () {
    var loadValine = function () {
      new Valine({
        el: '#valine',
        app_id: "833C0IwLe9o8QE1pN25Mv59F-gzGzoHsz",
        app_key: "YLQZdn7egrZOmJnwr6YFEw0y",
        placeholder: "雁过留痕",
        avatar: "mp",
        pageSize: "10",
        lang: "zh-CN",
      });
    }
    if ( true ) {
      $("#comments-btn").on("click", function () {
        $(this).hide();
        loadValine();
      });
    } else {
      loadValine();
    }
  };
</script>

</section>
      
    </section>
  </div>
</article></div>
      <div class="col-xl-3">
        
          
  <aside class="toc-wrap">
    <h3 class="toc-title">文章目录：</h3>
    <ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#Spring"><span class="toc-text">Spring</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#spring%E6%A1%86%E6%9E%B6"><span class="toc-text">spring框架</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%BB%80%E4%B9%88%E6%98%AFIOC"><span class="toc-text">什么是IOC</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#IOC-%E5%AE%B9%E5%99%A8%E5%AE%9E%E7%8E%B0%E4%B8%A4%E7%A7%8D%E6%96%B9%E5%BC%8F"><span class="toc-text">IOC 容器实现两种方式</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%BB%80%E4%B9%88%E6%98%AFAOP"><span class="toc-text">什么是AOP</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Aop%E7%9A%84%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86"><span class="toc-text">Aop的底层原理</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#AOP%E7%9A%84%E4%B8%93%E4%B8%9A%E6%9C%AF%E8%AF%AD"><span class="toc-text">AOP的专业术语</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#AOP%E5%AE%9E%E7%8E%B0%E6%97%A5%E5%BF%97%E6%94%B6%E9%9B%86"><span class="toc-text">AOP实现日志收集</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BB%9F%E4%B8%80%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86"><span class="toc-text">统一异常处理</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Spring%E4%B8%AD%E7%9A%84%E4%BA%8B%E5%8A%A1%E6%98%AF%E6%80%8E%E4%B9%88%E5%AE%9E%E7%8E%B0%E7%9A%84"><span class="toc-text">Spring中的事务是怎么实现的</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Spring%E4%B8%AD%E4%BA%8B%E5%8A%A1%E5%A4%B1%E6%95%88%E7%9A%84%E5%9C%BA%E6%99%AF"><span class="toc-text">Spring中事务失效的场景</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Spring%E4%BA%8B%E5%8A%A1%E7%9A%84%E4%BC%A0%E6%92%AD"><span class="toc-text">Spring事务的传播</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Spring-Bean%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F"><span class="toc-text">Spring Bean的生命周期</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Spring%E4%B8%AD%E7%9A%84%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96"><span class="toc-text">Spring中的循环依赖</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#SpringMvc%E7%9A%84%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B"><span class="toc-text">SpringMvc的执行流程</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#SpringBoot%E7%9A%84%E8%87%AA%E5%8A%A8%E8%A3%85%E9%85%8D"><span class="toc-text">SpringBoot的自动装配</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Sping%EF%BC%8CSpringMvc-SpringBoot%E7%9A%84%E5%B8%B8%E8%A7%81%E6%B3%A8%E8%A7%A3"><span class="toc-text">Sping，SpringMvc,SpringBoot的常见注解</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Mybatis"><span class="toc-text">Mybatis</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#MyBatis%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B"><span class="toc-text">MyBatis执行流程</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Mybatis%E7%9A%84%E5%BB%B6%E8%BF%9F%E5%8A%A0%E8%BD%BD%E5%92%8C%E4%B8%80%E4%BA%8C%E7%BA%A7%E7%BC%93%E5%AD%98"><span class="toc-text">Mybatis的延迟加载和一二级缓存</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Mybatis%E7%9A%84-he-%E7%9A%84%E5%8C%BA%E5%88%AB"><span class="toc-text">Mybatis的#{}he ${}的区别</span></a></li></ol></li></ol>
  </aside>

        
      </div>
    </div>
  </div>
</main>
  

<footer class="footer">
  <div class="footer-social"><a 
        href="tencent://message/?Menu=yes&uin=2911890696 "
        target="_blank"
        class="footer-social-item"
        onMouseOver="this.style.color= '#12B7F5'" 
        onMouseOut="this.style.color='#33333D'">
          <i class="iconfont  iconQQ "></i>
      </a><a 
        href="javascript:; "
        target="_blank"
        class="footer-social-item"
        onMouseOver="this.style.color= '#09BB07'" 
        onMouseOut="this.style.color='#33333D'">
          <i class="iconfont  iconwechat-fill "></i>
      </a><a 
        href="https://github.com/nuban "
        target="_blank"
        class="footer-social-item"
        onMouseOver="this.style.color= '#9f7be1'" 
        onMouseOut="this.style.color='#33333D'">
          <i class="iconfont  icongithub-fill "></i>
      </a></div>
  
    <div class="footer-copyright"><p>Powered by <a target="_blank" href="https://gitee.com/comebackearly">东子</a>  | 爷-青结~~</p></div>
  
  <link rel="stylesheet" href="/dist/APlayer.min.css">
  <div id="aplayer"></div>
  <script type="text/javascript" src="/dist/APlayer.min.js"></script>
  <script type="text/javascript" src="/dist/music.js"></script>
  
</footer>
  
      <div class="fab fab-plus">
    <i class="iconfont iconplus"></i>
  </div>
  
  
  
  <div class="fab fab-up">
    <i class="iconfont iconcaret-up"></i>
  </div>
  
  
  
    
<script src="/js/color-mode.js"></script>

  
  
</body>

<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>





  
<script src="https://cdn.bootcdn.net/ajax/libs/jquery.lazyload/1.9.1/jquery.lazyload.min.js"></script>




  
<script src="https://cdnjs.cloudflare.com/ajax/libs/fancybox/3.5.7/jquery.fancybox.min.js"></script>






  
<script src="https://cdn.bootcdn.net/ajax/libs/jquery.qrcode/1.0/jquery.qrcode.min.js"></script>




<script src="/js/utils.js"></script>
<script src="/js/script.js"></script>







  <script>
    (function () {
      var bp = document.createElement('script');
      var curProtocol = window.location.protocol.split(':')[0];
      if (curProtocol === 'https') {
        bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
      } else {
        bp.src = 'http://push.zhanzhang.baidu.com/push.js';
      }
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(bp, s);
    })();
  </script>



  <script defer src='https://hm.baidu.com/hm.js?dda12e485d1b1d3113db8147f482111c'></script>











</html>